package com.aboverock.core.aop;

import com.aboverock.autoconfigure.util.JwtUtil;
import com.aboverock.common.enums.BrowserType;
import com.aboverock.common.enums.LogTypeEnum;
import com.aboverock.common.utils.BrowserUtils;
import com.aboverock.common.utils.ClientIpUtils;
import com.aboverock.core.spring.SpringWebHolder;
import com.aboverock.module.system.domain.Log;
import com.aboverock.module.system.service.LogService;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 使用AspectJ实现异常日志AOP.
 *
 * @author Rock Wang
 */
@Component
@Aspect
public class RuntimeExceptionLogAspect {

    private static Logger logger = LoggerFactory.getLogger(RuntimeExceptionLogAspect.class);

    @Autowired
    private LogService logService;

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Pointcut("execution(* com.aboverock..*Controller.*(..))")
    public void systemPointcut() {

    }

    @Pointcut("execution(* com.aboverock.core.exception.handler.SystemExceptionHandler.*(..))")
    public void systemExceptionHandlerPointcut() {

    }

    @Before("systemPointcut() || systemExceptionHandlerPointcut()")
    public void doBefore() {
        startTime.set(System.currentTimeMillis());
    }

    /**
     * 异常记录.
     *
     * @param exception the exception
     */
    @AfterThrowing(pointcut = "systemPointcut() || systemExceptionHandlerPointcut()", throwing = "exception")
    public void doAfterThrowing(RuntimeException exception) {
        if (exception instanceof ValidationException) {
            throw exception;
        }

        logger.error(exception.getMessage(), exception);

        Long endTime = System.currentTimeMillis();

        Log log = new Log();
        log.setType(LogTypeEnum.EXCEPTION.getValue());

        HttpServletRequest request = SpringWebHolder.getRequest();
        String loginName = JwtUtil.getLoginName();
        log.setLoginName(loginName);

        String ip = ClientIpUtils.getIpAddr(request);
        log.setIp(ip);

        BrowserType browserType = BrowserUtils.getBrowserType(request);
        log.setBrowserType(null == browserType ? "Unknown" : browserType.toString());

        log.setRequest(request.getRequestURI());
        log.setHttpMethod(request.getMethod());

        log.setRequestTime(new Date(startTime.get()));
        log.setOperationTime(String.valueOf((endTime - startTime.get()) / 1000.0));
        log.setDetails(exception.toString());

        startTime.remove();
        if (logger.isDebugEnabled()) {
            logger.debug("user:{}, request url：{} ,  time-consuming：{}s, exception：{}.",
                    new Object[] { loginName, log.getRequest(), log.getOperationTime(),
                        exception });
        }
        logService.save(log);
    }

    protected Throwable findRootExceptionThrowable(Exception e) {
        Throwable rootCause = e;

        while (rootCause.getCause() != null) {
            rootCause = rootCause.getCause();
        }
        return rootCause;
    }
}
